iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0
Modern Web

自己開發一個~?系列 第 29

Springboot~做RBAC

  • 分享至 

  • xImage
  •  

ㄟ害~剩下一天
/images/emoticon/emoticon06.gif

先用POSTMAN測試http://localhost:8080/允許

測試http://localhost:8080/api/hello/helloworld/rawdata就沒有開放

修改ApplictionConfig程式碼

package com.tzu.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;

//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
	//Attribute 使用spEL ${}
	//@Value標註取出預設組態application.properties設定項目
	@Value("${spring.datasource.url}")
	private String url;
	@Value("${spring.datasource.username}")
	private String userName;
	@Value("${spring.datasource.password}")
	private String password;
	
	public ApplictionConfig() {
		System.out.println("Configuration Bean配置了");
	}

	//生產一個HelloBean物件
	@Bean(name="hellonean")
	public HelloBean getHelloBean() {
		System.out.println("Hello Bean產生了");
		//建構HelloBean
		HelloBean hello=new HelloBean();
		return hello;
	}
	
	@Bean
	public TWHello getTWHello() {
		System.out.println("TW Hello Bean產生了");
		//建構HelloBean
		TWHello hello=new TWHello();
		return hello;
	}
	
	
	
	
	//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
	@Bean
	public HelloProxy getHelloProxy(TWHello bean) {
		var helloProxy=new HelloProxy(bean);
		return helloProxy;
	}
	
	//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
	@Bean
	@Scope("singleton")
	public DataSource createDataSource() {
		System.out.println("Datasource:"+this.url);
		//建構MySQLDataSource
		MysqlDataSource datasource=new MysqlDataSource();
		//配置要件 URL/User name/password
		datasource.setUrl(url);
		datasource.setUser(userName);
		datasource.setPassword(password);
		
		//Driver 會進行內部使用
		return datasource;
	}
	//生產JdbcTemplate元件(Spring Bean)
		//透過IoC注入控制反轉 注入DataSource物件
		@Bean //生命週期每一個注入 產生一個體
		public JdbcTemplate createJdbcTemplate(DataSource datasource) {
			//建構JdbcTemplate物件
			System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
			JdbcTemplate template=new JdbcTemplate();
			template.setDataSource(datasource); //Property Injection屬性注入依賴物件
			return template;
		}
		//註冊Filter bean 進入Spring Container
		//使用IoC 注入控制反轉 注入需要JpaRepository
		@Bean
		public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
			System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
			FilterRegistrationBean<ApiKeyFilter> register=
					new FilterRegistrationBean<>();
			var filter=new ApiKeyFilter();
			filter.setReposit(reposit); //透過Property Injection
			register.setFilter(filter); //註冊Filter
			
			//設定url pattern
			register.addUrlPatterns("/api/customers/*"); //設定Filter UrlPattern 攔截起來的端點
			return register;
			
					
		}
		//產生Web Security Config
		//使用注入控制反轉 (IoC)
		@Bean
		public SecurityFilterChain configSecurity(HttpSecurity http) {
			System.out.println(http.toString());
			//設定CSRF 安全性 產生403 status
			SecurityFilterChain chain=null;
			
			try {
				http.csrf()
				.and();
				http.formLogin().loginPage("/mylogin")
				.and()
				.securityMatcher("/**")
				.authorizeHttpRequests()
				.requestMatchers("/","/mylogin","/api/hello/**","/api/member/**").permitAll() //允許匿名存取(只允許網站入口-首頁/燈入頁面
				.requestMatchers("/css/**","/html/**","/js/**","/ui/**").permitAll(); //針對static file允續匿名存取
				
				
				//.anyRequest()
				//.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
				
				
				//... 匿名存取網站 通通可以通行
				
				//透過HttpSecurity Builder(工廠)生產出SecurityFilterChain
				chain=http.build();
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			return chain;
		}
	}

測試http://localhost:8080/api/hello/helloworld/rawdata
就有開放
你的 Spring Boot 配置類(ApplictionConfig)有一些複製貼上的問題,重複定義了相同的類。請將其整理成如下的配置:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;

import javax.sql.DataSource;

@EnableWebSecurity
@Configuration
public class ApplicationConfig {
    @Value("${spring.datasource.url}")
    private String url;

    @Value("${spring.datasource.username}")
    private String userName;

    @Value("${spring.datasource.password}")
    private String password;

    @Bean(name = "helloBean")
    public HelloBean getHelloBean() {
        return new HelloBean();
    }

    @Bean
    public TWHello getTWHello() {
        return new TWHello();
    }

    @Bean
    public HelloProxy getHelloProxy(TWHello bean) {
        return new HelloProxy(bean);
    }

    @Bean
    @Scope("singleton")
    public DataSource createDataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();
        dataSource.setUrl(url);
        dataSource.setUser(userName);
        dataSource.setPassword(password);
        return dataSource;
    }

    @Bean
    public JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    @Bean
    public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository repository) {
        FilterRegistrationBean<ApiKeyFilter> registrationBean = new FilterRegistrationBean<>();
        ApiKeyFilter filter = new ApiKeyFilter();
        filter.setReposit(repository);
        registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns("/api/customers/*");
        return registrationBean;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable() // 禁用 CSRF
            .authorizeRequests()
                .antMatchers("/", "/mylogin", "/css/**", "/html/**", "/js/**", "/ui/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/mylogin");
        return http.build();
    }
}

這樣的配置更整潔,並消除了重複定義的問題。請確保各個依賴的類都在相應的包下並被正確引入。

依角色建立AppUsers.java 檔案:使用Users.Buildr 朝狀類別 工廠模式類別

package com.tzu.domain;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class AppUsers implements UserDetailsService{
	//attribute
	private String userName;
	private String password;
	private String[] roles;
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
		return null;
	}
}

這段Java程式碼是一個類別 AppUsers,看起來它是實現了Spring Security框架中的 UserDetailsService 介面。這個介面通常用於處理用戶驗證和授權的相關邏輯。讓我解釋一下這段程式碼的主要部分:

  1. package com.tzu.domain;:這是Java程式碼的包聲明,它指定了這個類別的所屬包。

  2. import 陳述句:這些 import 陳述句引入了所需的外部類別,包括 UserDetailsUserDetailsService,它們來自Spring Security框架。它們是用於處理用戶身份驗證和授權的關鍵介面。

  3. public class AppUsers implements UserDetailsService:這是 AppUsers 類別的定義,它實現了 UserDetailsService 介面,這意味著該類別需要實現 loadUserByUsername 方法,以處理用戶驗證。

  4. private String userName;private String password;private String[] roles;:這些是類別的私有屬性,它們用於存儲用戶的名稱、密碼和角色信息。

  5. public UserDetails loadUserByUsername(String username) 方法:這是實現 UserDetailsService 介面所必需的方法。它用於根據提供的使用者名稱(username)查找和返回相應的用戶詳細信息(UserDetails)。在目前的程式碼中,方法內部尚未實作,只是返回了 null

通常,你需要在 loadUserByUsername 方法中實作用戶驗證邏輯,包括檢查使用者名稱和密碼,以及分配角色等操作。這個程式碼片段只是一個初始框架,你需要根據你的應用程序需求來實現更具體的邏輯。

按右鍵產生get跟set

打V

顯示

package com.tzu.domain;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class AppUsers implements UserDetailsService{
	//attribute
	private String userName;
	private String password;
	private String[] roles;
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		
		return null;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String[] getRoles() {
		return roles;
	}
	public void setRoles(String[] roles) {
		this.roles = roles;
	}
}

一個Java類,可能用於Spring Security框架中。這個類似乎是用於處理使用者認證和授權的一部分。讓我為您解釋其中的一些關鍵點:

  1. AppUsers 類實現了 UserDetailsService 介面,這表明它是一個自定義的使用者詳細資訊服務類,用於 Spring Security 的使用者認證。

  2. loadUserByUsername 方法中,您需要實現根據使用者名稱載入使用者詳細資訊的邏輯。通常,您會從資料庫或其他資料儲存中檢索使用者的使用者名稱、密碼和角色資訊,並返回一個實現了 UserDetails 介面的物件。這個物件通常包含有關使用者的認證和授權資訊。

  3. 類中包括了使用者名稱(userName)、密碼(password)和角色(roles)屬性,這些屬性可能在 loadUserByUsername 方法中被設定為從資料來源中檢索到的值。

  4. 使用者名稱和密碼是使用者的認證憑據,而角色通常用於使用者的授權,以確定使用者是否有訪問某些受保護資源的許可權。

請注意,這只是一個類的一部分,要使它完全運作,您需要實現 loadUserByUsername 方法中的邏輯,並配置Spring Security以使用您的AppUsers類來處理使用者認證和授權。通常,Spring Security需要與資料庫或其他身份驗證和授權資料來源整合,以驗證使用者並控制其訪問許可權。

修改AppUsers.java 檔案:使用Users.Buildr 朝狀類別 工廠模式類別

package com.tzu.domain;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

//封裝使用者登入驗證 OK識別物件(包含有UserName/Password/Roles)
public class AppUsers implements UserDetailsService{
	//attribute
	private String userName;
	private String password;
	private String[] roles;
	//屬性注入Property Injection
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		//使用Users.Buildr 朝狀類別 工廠模式類別
		UserDetails userDetails=User.builder()
		.password(password)
		.username(username)
		.roles(roles)
		.build();
		return userDetails;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String[] getRoles() {
		return roles;
	}

	public void setRoles(String[] roles) {
		this.roles = roles;
	}
}

這段Java程式碼是一個類別 AppUsers,它實現了Spring Security框架中的 UserDetailsService 介面,並提供了一個簡單的用戶登入驗證機制。讓我解釋一下這段程式碼的主要部分:

  1. private String userName;private String password;private String[] roles;:這些是類別的私有屬性,分別用於存儲用戶的名稱、密碼和角色信息。

  2. public UserDetails loadUserByUsername(String username) 方法:這是實現 UserDetailsService 介面所必需的方法。它根據提供的使用者名稱(username)建立一個 UserDetails 物件,其中包括使用者的名稱、密碼和角色。這個方法使用了Spring Security提供的 User.builder() 工廠模式來創建 UserDetails 物件,然後返回該物件。

  3. public String getUserName()public void setUserName(String userName)public String getPassword()public void setPassword(String password)public String[] getRoles()public void setRoles(String[] roles):這些是用於設定和獲取 userNamepasswordroles 屬性的 getter 和 setter 方法。

這個程式碼片段的主要功能是封裝了用戶的登入資訊,包括使用者名稱、密碼和角色,然後在 loadUserByUsername 方法中根據提供的使用者名稱返回相應的用戶詳細信息。這個類別似乎用於自訂用戶登入驗證邏輯,可以在Spring Security配置中使用。根據需要,你可以進一步擴展這個類別以滿足你的應用程序需求。

打開資料庫:新增會員資料表

CREATE TABLE `sakila`.`members` (
  `username` VARCHAR(30) NOT NULL,
  `password` VARCHAR(45) NOT NULL,
  `roles` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`username`))
COMMENT = '會員資料表';

先填入一個資料

再新增一個AppUserHandler.java檔案:做RBAC
以角色為基礎的存取控制[1][2](英語:Role-based access controlRBAC),是資訊安全領域中,一種較新且廣為使用的存取控制機制,其不同於強制存取控制以及自由選定存取控制[3]直接賦予使用者權限,而是將權限賦予角色

package com.tzu.domain;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class AppUserHandler implements UserDetailsService {

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// TODO Auto-generated method stub
		return null;
	}

}

這個程式碼片段是一個Java類別 AppUserHandler,它實現了Spring Security框架中的 UserDetailsService 介面,但在 loadUserByUsername 方法中尚未實作具體的用戶驗證邏輯。讓我幫你理解這段程式碼的主要要點:

  1. import 陳述句:這些 import 陳述句引入了Spring Security框架中所需的類別,包括 UserDetailsUserDetailsService,這些是用於處理用戶身份驗證和授權的介面。

  2. public UserDetails loadUserByUsername(String username) 方法:這是實現 UserDetailsService 介面所必需的方法。然而,在目前的程式碼中,方法內部只有一個註解 // TODO Auto-generated method stub,表示該方法尚未實作。

這個 AppUserHandler 類別似乎是設計用來處理用戶身份驗證的部分,但你需要實作 loadUserByUsername 方法,以根據提供的使用者名稱(username)來查找並返回相應的用戶詳細信息。通常,這個方法會檢查用戶名稱和密碼,然後返回一個實現 UserDetails 介面的物件,其中包含用戶的資訊和角色。這個類別的實際用途和邏輯需要根據你的應用程序需求進一步實作。

再新增一個MemberIdentity.java檔案:

package com.tzu.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;

@Entity
@Table(name="members")
public class MemberIdentity {
	//應對欄位的描述
		
		private String userName;
		
		private String password;
		
		private String roles;
}

右鍵做get跟set

都打V

加入應對欄位的描述

package com.tzu.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name="members")
public class MemberIdentity {
	//應對欄位的描述
		@Id
		@Column(name="username")
		private String userName;
		@Column(name="password")
		private String password;
		@Column(name="roles")
		private String roles;
		public String getUserName() {
			return userName;
		}

		public void setUserName(String userName) {
			this.userName = userName;
		}

		public String getPassword() {
			return password;
		}

		public void setPassword(String password) {
			this.password = password;
		}

		public String getRoles() {
			return roles;
		}

		public void setRoles(String roles) {
			this.roles = roles;
		}
		
}

這段程式碼是一個Java類別 MemberIdentity,它使用Jakarta Persistence API(曾稱為Java Persistence API,JPA)標記為實體,並用於映射到資料庫表格。讓我解釋一下這段程式碼的主要要點:

  1. @Entity:這是JPA的標記(Annotation),表示這個類別是一個實體,可以被映射到資料庫中的表格。

  2. @Table(name="members"):這是另一個JPA標記,指定了將這個實體映射到名稱為 "members" 的資料庫表格。

  3. @Id:這是JPA的標記,指定 userName 屬性是這個實體的主鍵,也就是資料庫表格的主索引鍵。

  4. @Column(name="username")@Column(name="password")@Column(name="roles"):這些是用來對應Java類別的屬性到資料庫表格欄位的JPA標記。它們指定了每個屬性的名稱,這些名稱與資料庫表格的欄位名稱相對應。

  5. private String userName;private String password;private String roles;:這些是類別的私有屬性,分別代表了資料庫表格中的欄位,例如使用者名稱、密碼和角色。

  6. public getter 和 setter 方法:這些方法用於設定和獲取 userNamepasswordroles 屬性的值,這是將Java物件的屬性映射到資料庫表格欄位的方式。

總結來說,這個程式碼片段定義了一個JPA實體 MemberIdentity,並設定了它的資料庫表格映射,以便在應用程序中使用該實體來對應資料庫中的成員(會員)記錄。

再新增一個MemberJpaRepository.java檔案:

package com.tzu.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MemberJpaRepository extends JpaRepository<MemberIdentity,String>{
	public MemberIdentity findByUserName(String userName);
}

這段程式碼是一個Spring Data JPA的倉庫介面 MemberJpaRepository,它用於對應到 MemberIdentity 實體類別,並提供了資料存取的方法。讓我解釋一下這段程式碼的主要要點:

  1. import 陳述句:這些 import 陳述句引入了Spring框架相關的類別,包括 JpaRepositoryRepository

  2. @Repository:這是一個Spring框架的註解,它將這個介面標記為一個Spring的存儲庫(repository),用於提供資料存取的功能。

  3. public interface MemberJpaRepository extends JpaRepository<MemberIdentity, String>:這個介面宣告了 MemberJpaRepository,並繼承了 JpaRepository 介面,這表示它將提供一組預設的CRUD(建立、讀取、更新、刪除)操作,針對 MemberIdentity 實體,使用 String 作為主鍵的資料型別。

  4. public MemberIdentity findByUserName(String userName):這是一個自訂的查詢方法,透過 findByUserName 方法名稱來指定,它將根據使用者名稱(userName)來查找成員(會員)記錄。Spring Data JPA會根據方法名稱的約定自動生成查詢。

這個介面 MemberJpaRepository 提供了對 MemberIdentity 實體的常見資料存取操作,並且你可以使用它來輕鬆地執行資料庫查詢和更新操作,而不必手動編寫SQL查詢語句。這有助於簡化資料存取層的開發工作。

再加入一個MemberService檔案

package com.tzu.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.tzu.domain.MemberJpaRepository;

@RestController
public class MemberService {
	//注入依賴的Jpa
		@Autowired
		private MemberJpaRepository memberRepo;
		
		//使用登入驗證作業
		@GetMapping(path="/api/member/uservalid/{username}/{pwd}/rawdata",produces="application/json")
		public String userVaid(@PathVariable(name="username")String userName,@PathVariable(name="pwd")String password) {
			return userName+" "+memberRepo;
		}

}

這段程式碼是一個Spring Boot應用程序中的REST控制器 MemberService,它使用Spring框架和Spring Boot來處理HTTP請求。讓我解釋這段程式碼的主要要點:

  1. import 陳述句:這些 import 陳述句引入了Spring框架和Spring Boot相關的類別,包括 AutowiredGetMappingRestController,以及 MemberJpaRepository

  2. @RestController:這是一個Spring Boot註解,標記這個類別為一個REST控制器,它將處理HTTP請求並返回JSON格式的響應。

  3. @Autowired:這是Spring框架的註解,用於進行依賴注入。在這個情況下,它將 MemberJpaRepository 類別注入到 memberRepo 屬性中,以便在這個控制器中使用。

  4. @GetMapping:這是一個Spring Boot註解,指定了這個方法應該處理HTTP GET請求。path 屬性指定了請求的路徑,produces 屬性指定了回傳的資料格式。

  5. public String userValid(@PathVariable(name="username") String userName, @PathVariable(name="pwd") String password):這是一個HTTP GET請求的處理方法,它接受兩個路徑變數 usernamepwd,分別代表使用者名稱和密碼。在這個方法中,它返回了使用者名稱和 memberRepo 的字串表示,但是似乎還未執行實際的登入驗證邏輯。

總之,這個控制器 MemberService 負責處理HTTP GET請求,並透過注入的 MemberJpaRepository 來處理會員資料的操作,但在目前的程式碼中,登入驗證邏輯尚未被實作,所以還需要進一步開發以實現登入驗證的功能。

再修改ApplictionConfig.java

package com.tzu.config;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;

import com.mysql.cj.jdbc.MysqlDataSource;
import com.tzu.beans.HelloBean;
import com.tzu.beans.HelloProxy;
import com.tzu.beans.IHello;
import com.tzu.beans.TWHello;
import com.tzu.domain.ApiKeyRepository;
import com.tzu.filter.ApiKeyFilter;

//透過方法生產Bean物件 註冊到Spring容器去
@EnableWebSecurity
@Configuration
public class ApplictionConfig {
	//Attribute 使用spEL ${}
	//@Value標註取出預設組態application.properties設定項目
	@Value("${spring.datasource.url}")
	private String url;
	@Value("${spring.datasource.username}")
	private String userName;
	@Value("${spring.datasource.password}")
	private String password;
	
	public ApplictionConfig() {
		System.out.println("Configuration Bean配置了");
	}

	//生產一個HelloBean物件
	@Bean(name="hellonean")
	public HelloBean getHelloBean() {
		System.out.println("Hello Bean產生了");
		//建構HelloBean
		HelloBean hello=new HelloBean();
		return hello;
	}
	
	@Bean
	public TWHello getTWHello() {
		System.out.println("TW Hello Bean產生了");
		//建構HelloBean
		TWHello hello=new TWHello();
		return hello;
	}
	
	
	
	
	//參數使用定義Bean alias Name 注入依賴 隨著窗口物件注入到對方去 進行反轉物件注入
	@Bean
	public HelloProxy getHelloProxy(TWHello bean) {
		var helloProxy=new HelloProxy(bean);
		return helloProxy;
	}
	
	//產生一個DataSource 是共用的物件(連接物件工廠 整個應用系統工廠只要一個即可)
	@Bean
	@Scope("singleton")
	public DataSource createDataSource() {
		System.out.println("Datasource:"+this.url);
		//建構MySQLDataSource
		MysqlDataSource datasource=new MysqlDataSource();
		//配置要件 URL/User name/password
		datasource.setUrl(url);
		datasource.setUser(userName);
		datasource.setPassword(password);
		
		//Driver 會進行內部使用
		return datasource;
	}
	//生產JdbcTemplate元件(Spring Bean)
		//透過IoC注入控制反轉 注入DataSource物件
		@Bean //生命週期每一個注入 產生一個體
		public JdbcTemplate createJdbcTemplate(DataSource datasource) {
			//建構JdbcTemplate物件
			System.out.println("JdbcTemplate 注入的DataSource:"+datasource.toString());
			JdbcTemplate template=new JdbcTemplate();
			template.setDataSource(datasource); //Property Injection屬性注入依賴物件
			return template;
		}
		//註冊Filter bean 進入Spring Container
		//使用IoC 注入控制反轉 注入需要JpaRepository
		@Bean
		public FilterRegistrationBean<ApiKeyFilter> apikeyFilter(ApiKeyRepository reposit) {
			System.out.println("註冊Filter攔截器 ApiKeyFilter..."+reposit);
			FilterRegistrationBean<ApiKeyFilter> register=
					new FilterRegistrationBean<>();
			var filter=new ApiKeyFilter();
			filter.setReposit(reposit); //透過Property Injection
			register.setFilter(filter); //註冊Filter
			
			//設定url pattern
			register.addUrlPatterns("/api/customers/*"); //設定Filter UrlPattern 攔截起來的端點
			return register;
			
					
		}
		//產生Web Security Config
		//使用注入控制反轉 (IoC)
		@Bean
		public SecurityFilterChain configSecurity(HttpSecurity http) {
			System.out.println(http.toString());
			//設定CSRF 安全性 產生403 status
			SecurityFilterChain chain=null;
			
			try {
				http.csrf()
				.and();
				http.formLogin().loginPage("/mylogin")
				.and()
				.securityMatcher("/**")
				.authorizeHttpRequests()
				.requestMatchers("/","/mylogin","/api/hello/**","/api/member/**").permitAll() //允許匿名存取(只允許網站入口-首頁/燈入頁面
				.requestMatchers("/css/**","/html/**","/js/**","/ui/**").permitAll(); //針對static file允續匿名存取
				
				
				//.anyRequest()
				//.authenticated(); //調用到登入頁面 登入頁面並沒有匿名存取 又要求到自己一次....
				
				
				//... 匿名存取網站 通通可以通行
				
				//透過HttpSecurity Builder(工廠)生產出SecurityFilterChain
				chain=http.build();
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				System.out.println(e.getMessage());
			}
			
			return chain;
		}
	}

這段程式碼是一個Spring Boot應用程式的配置類別 ApplictionConfig,負責配置應用程式中的Spring Beans以及安全性設定。以下是這段程式碼的主要要點:

  1. @Configuration:這是Spring Framework的註解,標記這個類別為配置類別,告訴Spring容器這個類別包含了Bean的定義。

  2. @Value:這是Spring框架的註解,用於注入屬性值,通常是從 application.properties 文件中讀取的屬性值。

  3. @Bean:這是Spring Framework的註解,用於定義Bean的方法。在這個類別中,有多個 @Bean 方法,用於建立不同的Bean物件。

  4. 注入依賴:透過 @Autowired,你可以將依賴的Bean注入到其他Bean中。例如,createJdbcTemplate 方法注入了一個 DataSource 物件,並用於建立 JdbcTemplate Bean。

  5. 註冊Filter:apikeyFilter 方法註冊了一個名為 ApiKeyFilter 的Filter,並指定了要攔截的URL模式。

  6. 安全性配置:configSecurity 方法配置了應用程式的安全性,包括CSRF安全性、登入頁面、以及哪些URL可以匿名存取。這部分看起來是針對Spring Security的配置。

總之,這個配置類別主要負責設定Spring Beans、資料庫連接、Filter以及應用程式的安全性。這些Beans和設定將被Spring容器管理,以協助你的應用程式運作。然而,還有一些錯誤在程式碼中,例如 @EnableWebSecurityhttp.securityMatcher 應該是 http.antMatcher。請確保這些錯誤被修正以使應用程式正確運行。

用POSTMAN測試http://localhost:8080/api/member/uservalid/eric/1111/rawdata

修改AppUserHandler 程式碼

package com.tzu.domain;

import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

//進行會員資料查詢 借助屬性注入JPA 
public class AppUserHandler implements UserDetailsService {
	private MemberJpaRepository memberRepo;
	//透過屬性注入setXxxx
	

	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		MemberIdentity member=memberRepo.findByUserName(username);
		UserDetails userDetails=null;
		//判斷是否有找到
		if(member==null) {
			throw new UsernameNotFoundException("查無該使用者帳戶");
		}else {
			//產生回應的UserDetails
			String[] roles=member.getRoles().split(",");
			userDetails=User.builder()
					.username(member.getUserName())
					.password(member.getPassword())
					.roles(roles)
					.build();
			
		}
		return userDetails;
	}

	public void setMemberRepo(MemberJpaRepository memberRepo) {
		this.memberRepo = memberRepo;
	}

}

這段程式碼是一個實作Spring Security的 UserDetailsService 介面的類別 AppUserHandler,它負責處理使用者登入驗證,並借助屬性注入 MemberJpaRepository 以查詢會員資料。以下是這段程式碼的主要要點:

  1. UserDetailsService 介面:這個類別實作了Spring Security的 UserDetailsService 介面,該介面是Spring Security用於查詢使用者資料的一個關鍵部分。實作這個介面需要實現 loadUserByUsername 方法。

  2. MemberJpaRepository:這是一個使用了JPA的資料存取介面,用於查詢會員資料。在這個程式碼中, memberRepo 屬性透過屬性注入(setter注入)方式被設定。

  3. loadUserByUsername 方法:這是實作 UserDetailsService 介面所必須的方法。當使用者嘗試登入時,Spring Security會呼叫這個方法,傳遞使用者提供的使用者名稱(username)。在這個方法中,程式碼試圖根據使用者名稱從資料庫中查詢會員資料。

  4. memberRepo.findByUserName(username):這個方法呼叫了 memberRepofindByUserName 方法,以根據使用者名稱查詢會員資料。如果找到了對應的會員,則會建立一個 UserDetails 物件,其中包括使用者名稱、密碼和角色資訊。

  5. 使用者未找到:如果找不到對應的會員資料,則會拋出 UsernameNotFoundException 例外,表示未找到該使用者帳戶。

  6. 使用者找到:如果找到了會員資料,則會建立一個 UserDetails 物件,其中包括使用者名稱、密碼和角色資訊,然後將該物件返回,以供Spring Security使用。

總之,這個 AppUserHandler 類別實現了 UserDetailsService 介面,並透過 MemberJpaRepository 查詢會員資料,以進行使用者的登入驗證。

修改MemberService檔案

package com.tzu.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.tzu.domain.AppUserHandler;
import com.tzu.domain.MemberJpaRepository;

@RestController
public class MemberService {
	//注入依賴的Jpa
		@Autowired
		private MemberJpaRepository memberRepo;
		
		//使用登入驗證作業
		@GetMapping(path="/api/member/uservalid/{username}/{pwd}/rawdata",produces="application/json")
		public UserDetails userVaid(@PathVariable(name="username")String userName,@PathVariable(name="pwd")String password) {
			//建構自訂UserDetailsService 
			AppUserHandler userService=new AppUserHandler();
			//注入Jap
			userService.setMemberRepo(memberRepo);
			//透過服務取出UserDetails物件
			UserDetails userDetails=userService.loadUserByUsername(userName);
			
			return userDetails;
		}

}

這段程式碼是一個Spring Boot應用程式的REST控制器 MemberService,它用於處理HTTP請求並執行使用者的登入驗證。以下是這段程式碼的主要要點:

  1. @RestController:這是Spring Boot的註解,標記這個類別為一個REST控制器,它會處理HTTP請求並返回JSON格式的響應。

  2. @Autowired:這是Spring框架的註解,用於進行依賴注入。在這個情況下,它將 MemberJpaRepository 類別注入到 memberRepo 屬性中,以便在這個控制器中使用。

  3. @GetMapping:這是Spring Boot的註解,指定了這個方法應該處理HTTP GET請求。path 屬性指定了請求的路徑,produces 屬性指定了回傳的資料格式。

  4. userValid 方法:這是一個HTTP GET請求的處理方法,它接受兩個路徑變數 usernamepwd,分別代表使用者名稱和密碼。在這個方法中,它建立了一個 AppUserHandler 物件,然後注入了 memberRepo 依賴,接著呼叫 loadUserByUsername 方法進行使用者的登入驗證。最後,它將回傳的 UserDetails 物件作為JSON格式的回應。

總之,這個控制器 MemberService 負責處理HTTP GET請求,並透過 AppUserHandler 執行使用者的登入驗證,並返回使用者資料的詳細資訊,以JSON格式回應。這是一個簡單的範例,實際上,通常會使用Spring Security來處理使用者的登入驗證,而不是手動建立 AppUserHandler 物件。

目前資料庫有資料有兩個角色IT跟ACCT

用POSTMAN測試http://localhost:8080/api/member/uservalid/eric/1111/rawdata

被驗證通過/驗證通過管理員

再增加一個AppPrincipal檔案

package com.tzu.domain;

import org.springframework.security.core.AuthenticatedPrincipal;

//結合一個特定的使用者UserDetails 注入相對的UserName
public class AppPrincipal implements AuthenticatedPrincipal{
	//Data Field
	private String name;
	//建構子注入
	public AppPrincipal(String name) {
		this.name=name;
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return name;
	}

}

這段程式碼定義了一個名為 AppPrincipal 的類別,它實作了Spring Security的 AuthenticatedPrincipal 介面。這個類別用於代表一個特定使用者,提供使用者的名稱資訊。以下是這段程式碼的主要要點:

  1. AuthenticatedPrincipal 介面:這是Spring Security的介面,代表一個已驗證的主體(使用者)。 AppPrincipal 類別實作了這個介面,所以它可以代表一個已驗證的使用者。

  2. name 屬性:這是 AppPrincipal 類別的屬性,用於存儲使用者的名稱。這是在建立 AppPrincipal 物件時設定的。

  3. 建構子注入:這個類別有一個建構子,接受一個字串參數 name,這個參數代表使用者的名稱。建立 AppPrincipal 物件時,可以將使用者名稱傳遞給建構子以初始化 name 屬性。

  4. getName 方法:這個方法是 AuthenticatedPrincipal 介面的一部分,它返回使用者的名稱。在 AppPrincipal 類別中,這個方法實作了,並返回存儲在 name 屬性中的名稱。

總之,AppPrincipal 類別用於代表一個已驗證的使用者,提供使用者名稱資訊,以便在應用程式中使用。通常,當使用者成功登入後,Spring Security會建立一個 AppPrincipal 物件,代表該使用者,並將其傳遞到相關的部分以供應用程式使用。

再修改MemberService 檔案

package com.tzu.service;

import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.tzu.domain.AppPrincipal;
import com.tzu.domain.AppUserHandler;
import com.tzu.domain.MemberJpaRepository;

@RestController
public class MemberService {
	//注入依賴的Jpa
	@Autowired
	private MemberJpaRepository memberRepo;
	
	//使用登入驗證作業
	@GetMapping(path="/api/member/uservalid/{username}/{pwd}/rawdata",produces="application/json")
	public Collection<? extends GrantedAuthority> userVaid(@PathVariable(name="username")String userName,@PathVariable(name="pwd")String password) {
		//建構自訂UserDetailsService 
		AppUserHandler userService=new AppUserHandler();
		Collection<? extends GrantedAuthority> authorties=null;
		//注入Jap
		userService.setMemberRepo(memberRepo);
		//透過服務取出UserDetails物件
		UserDetails userDetails=userService.loadUserByUsername(userName);
		//進行雙重驗證
		if(userDetails==null) {
			//沒有這一個會員
		}else {
			//驗證密碼
			if(password.equals(userDetails.getPassword())) {
				//2.建構Principal(當事者)
				AppPrincipal principal=new AppPrincipal(userDetails.getUsername());
				//3.準備授權許可證
				authorties=userDetails.getAuthorities();
				//4.封裝當事者與使用者 與授權令牌 進入一個貫穿應用系統Context
				
				
			}else {
				//偽造身分
				
			}
		}
			
		
		return authorties;
	}

}

這段程式碼是一個Spring Boot應用程式的REST控制器 MemberService,它負責處理HTTP GET請求以進行使用者的登入驗證。這個控制器透過 AppUserHandler 類別執行使用者的登入驗證,並返回一個 Collection,其中包含了授權許可證(GrantedAuthority)。以下是這段程式碼的主要要點:

  1. @RestController:這是Spring Boot的註解,標記這個類別為一個REST控制器,用於處理HTTP GET請求。

  2. @Autowired:這是Spring框架的註解,用於進行依賴注入。在這個情況下,它將 MemberJpaRepository 類別注入到 memberRepo 屬性中,以便在這個控制器中使用。

  3. @GetMapping:這是Spring Boot的註解,指定了這個方法應該處理HTTP GET請求。path 屬性指定了請求的路徑,produces 屬性指定了回傳的資料格式。

  4. userValid 方法:這是一個HTTP GET請求的處理方法,它接受兩個路徑變數 usernamepwd,分別代表使用者名稱和密碼。在這個方法中,它建立了一個 AppUserHandler 物件,然後注入了 memberRepo 依賴,接著呼叫 loadUserByUsername 方法進行使用者的登入驗證。

  5. 雙重驗證:在這個方法中,進行了雙重驗證。首先,它檢查 UserDetails 物件是否為 null,如果是,表示沒有對應的會員。然後,它檢查提供的密碼是否與 UserDetails 中的密碼相符。如果密碼相符,則建立一個 AppPrincipal 物件,代表當事者,然後獲取授權許可證。

  6. 授權許可證:授權許可證是一個 Collection,其中包含了使用者的授權資訊(例如角色)。這些授權許可證可以用於控制使用者對應用程式中不同功能的存取。

總之,這個控制器 MemberService 負責處理HTTP GET請求,進行使用者的登入驗證,並返回包含授權許可證的 Collection。這是一個簡單的範例,實際上,通常會使用Spring Security來處理使用者的登入驗證,而不是手動建立 AppUserHandler 物件。

用POSTMAN測試http://localhost:8080/api/member/uservalid/eric/1111/rawdata

顯示角色
https://ithelp.ithome.com.tw/upload/images/20231019/20119035PqaqZjhgj2.png

https://ithelp.ithome.com.tw/upload/images/20231019/20119035bptf3KMNbL.png
謝謝大家收看
/images/emoticon/emoticon41.gif


上一篇
Springboot~security
下一篇
Springboot~明年見
系列文
自己開發一個~?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言